package com.acegear.horizon.controllers;

import com.acegear.horizon.controllers.vo.ResultVO;
import com.acegear.horizon.domain.models.User;
import com.acegear.horizon.domain.models.constraint.ClubMemberType;
import com.acegear.horizon.domain.models.jpa.ClubMember;
import com.acegear.horizon.domain.models.vo.UserInfoTokenVO;
import com.acegear.horizon.domain.repository.UserRepository;
import com.acegear.horizon.domain.repository.jpa.ClubMemberRepository;
import com.acegear.horizon.domain.services.SSOService;
import com.acegear.horizon.domain.services.UserService;
import com.acegear.horizon.utils.JwtTokenUtil;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.qiniu.util.Auth;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import io.jsonwebtoken.Claims;
import static com.acegear.horizon.controllers.vo.ResultVO.INVALIDATE_PASSWORD;

/**
 * Created by wangsike on 2016/12/13.
 */
@RestController
@RequestMapping("/auth")
public class AuthController {

    private static final ResultVO INVALIDATE_AUTH = null;

	@Autowired
    private UserRepository userRepository;

    @Autowired
    private UserService userService;

    @Autowired
    private SSOService ssoService;
    
    @Autowired
    private ClubMemberRepository clubMember;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    private static final String accessKey = "hAr7ttsrGWq-GL5qRea6ApmvIuLmbmu2jkj09fSW";
    private static final String secretKey = "uaY-42qUeEFpaLAiotjB9VgYn6X65cqAsvxCQsVi";
    private static final String bucket = "acegear-img";


    /**
     * 生成七牛token
     * @return
     */
    @RequestMapping(value = "/getQiNiuImageToken",method = RequestMethod.GET)
    public Map<String,Object> getQiNiuImageToken(){
        Map<String,Object> map = new HashMap<String,Object>();
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        map.put("imageToken",upToken);
        return map;
    }


    @RequestMapping(value = "/jwt", method = RequestMethod.GET)
    public ResultVO checkJwt(String jwt) {
        Claims claims = jwtTokenUtil.getClaimsFromToken(jwt);
        if (claims == null) {
            return ResultVO.WRONG_JWT;
        } else {
            Long userId = Long.parseLong(claims.getSubject());
            String accessToken = claims.getIssuer();
            Boolean isPass = userService.checkUserToken(userId, accessToken);
            Map<String, Object> map = new HashMap<>();
            map.put("userId", userId);
            map.put("success", isPass);
            return new ResultVO<>(map);
        }
    }

    @RequestMapping(value = "/jwt", method = RequestMethod.POST)
    @Transactional
    public ResultVO genJwt(Long userId, String accessToken) {
        Boolean isPass = userService.checkUserToken(userId, accessToken);
        if (isPass) {
            String jwt = jwtTokenUtil.generateToken(userId.toString(), accessToken, "");
            Map<String, Object> map = new HashMap<>();
            map.put("userId", userId);
            map.put("jwt", jwt);
            return new ResultVO<>(map);
        } else {
            return ResultVO.WRONG_ACCESS_TOKEN;
        }
    }

    /***
     * 手机号密码登录
     * @param phone
     * @param password
     * @return
     */
    @RequestMapping(value = "/login/phone", method = RequestMethod.POST)
    @Transactional
    public ResultVO phoneLogin(String phone, String password) {
        Optional<User> userOptional = userRepository.findByPhone(phone);
        if (userOptional.isPresent() && userOptional.get().passwordLogin(password).isPresent()) {
            User user = userOptional.get();
            List<ClubMember> clubNumber = clubMember.findByUserId(userOptional.get().getUserId());
            UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
            if(clubNumber.size()>0){
            	for (ClubMember clubMenbers: clubNumber) {
            		ClubMemberType rank = clubMenbers.getRank();
					if(rank.equals(ClubMemberType.ADMIN) || rank.equals(ClubMemberType.FOUNDER)){
						//俱乐部建立者和管理者能登录后台
						userInfoTokenVO.setCludId(clubMenbers.getClubId());
						userInfoTokenVO.setRegist(false);
						return new ResultVO<>(userInfoTokenVO);
					}else{
						return INVALIDATE_AUTH;
					}
				}
            }else{
            	return INVALIDATE_AUTH;
            }
        } else {
            return INVALIDATE_PASSWORD;
        }
		return INVALIDATE_AUTH;
    }

    /***
     * 邮箱密码登录
     * @param email
     * @param password
     * @return
     */
    @RequestMapping(value = "/login/email", method = RequestMethod.POST)
    @Transactional
    public ResultVO emailLogin(String email, String password) {
        Optional<User> userOptional = userRepository.findByEmail(email);
        if (userOptional.isPresent() && userOptional.get().passwordLogin(password).isPresent()) {
            User user = userOptional.get();
            UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
            userInfoTokenVO.setRegist(false);
            return new ResultVO<>(userInfoTokenVO);
        } else {
            return ResultVO.INVALIDATE_PASSWORD;
        }
    }

    @RequestMapping(value = "/logout", method = RequestMethod.POST)
    @Transactional
    public ResultVO<Object> logout(@RequestHeader("X-Consumer-Username") Long userId) {
        userRepository.findByUserId(userId).ifPresent(User::logout);
        return new ResultVO<>(null);
    }

    /***
     * 手机号密码注册
     * @param secureToken
     * @param phone
     * @param password
     * @param nickname
     * @param avatar
     * @return
     */
    @RequestMapping(value = "/register/phone", method = RequestMethod.GET)
    @Transactional
    public ResultVO phoneRegister(String secureToken, String phone, String password, String nickname, String avatar) {
        if (userService.checkSecureToken(secureToken, phone)) {
            Optional<User> userOptional =
                    User.phoneRegister(phone, password, nickname, avatar);
            return userOptional
                    .map(user -> {
                        UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
                        userInfoTokenVO.setRegist(true);
                        return userInfoTokenVO;
                    })
                    .map(ResultVO::new)
                    .orElse(ResultVO.PHONE_EXIST);
        } else {
            return ResultVO.SECURE_TOKEN_ERROR;
        }
    }

    /***
     * 邮箱密码注册
     * @param email
     * @param password
     * @param nickname
     * @param avatar
     * @return
     */
    @RequestMapping(value = "/register/email", method = RequestMethod.POST)
    @Transactional
    public ResultVO emailRegister(String email, String password, String nickname, String avatar) {
        Optional<User> userOptional =
                User.emailRegister(email, password, nickname, avatar);
        if (userOptional.isPresent()) {
            User user = userOptional.get();
            UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
            userInfoTokenVO.setRegist(true);
            return new ResultVO<>(userInfoTokenVO);
        } else {
            return ResultVO.EMAIL_EXIST;
        }
    }

    /***
     * 请求手机验证
     * @param phone
     * @return
     */
    @RequestMapping(value = "/verify/phone", method = RequestMethod.POST)
    @Transactional
    public ResultVO requestPhoneVerify(String phone) {
        Map<String, Boolean> result = new HashMap<>();
        if (userService.requestPhoneVerify(phone)) {
            result.put("send", true);
            return new ResultVO<>(result);
        } else {
            return ResultVO.SMS_SEND_FAILED;
        }

    }

    /***
     * 请求邮箱验证
     * @param email
     * @return
     */
    @RequestMapping(value = "/verify/email", method = RequestMethod.POST)
    @Transactional
    public ResultVO<Map<String, Boolean>> requestEmailVerify(String email) {
        Boolean send = userService.requestEmailVerify(email);
        Map<String, Boolean> result = new HashMap<>();
        result.put("send", send);
        return new ResultVO<>(result);
    }

    /***
     * 手机验证
     * @param phone
     * @param code
     * @return
     */
    @RequestMapping(value = "/verify/phone/{phone}", method = RequestMethod.PUT)
    @Transactional
    public ResultVO verifyPhone(@PathVariable String phone, String code) {
        Optional<String> token = userService.verifyPhone(phone, code);
        return tokenResult(token);
    }

    /***
     * 邮箱验证
     * @param email
     * @param code
     * @return
     */
    @RequestMapping(value = "/verify/email/{email}", method = RequestMethod.PUT)
    @Transactional
    public ResultVO verifyEmail(@PathVariable String email, String code) {
        Optional<String> token = userService.verifyEmail(email, code);
        return tokenResult(token);
    }

    /**
     * 微博验证
     */
    @RequestMapping(value = "/sso/weibo", method = RequestMethod.POST)
    @Transactional
    public ResultVO weiboLogin(String uid, String openToken) {
        if (userService.uidExist(uid)) {
            return userRepository
                    .findByUid(uid)
                    .flatMap(user1 -> user1.weiboLogin(openToken))
                    .map(user -> {
                        UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
                        userInfoTokenVO.setRegist(false);
                        return userInfoTokenVO;
                    })
                    .map(ResultVO::new)
                    .orElse(ResultVO.WEIBO_AUTH_FAILED);
        } else {
            return User.weiboRegister(uid, openToken)
                    .map(user -> {
                        UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
                        userInfoTokenVO.setRegist(true);
                        return userInfoTokenVO;
                    })
                    .map(ResultVO::new)
                    .orElse(ResultVO.WEIBO_AUTH_FAILED);
        }
    }

    @RequestMapping(value = "/sso/weibo/code", method = RequestMethod.POST)
    @Transactional
    public ResultVO weiboLoginCode(String code) {
        return ssoService
                .wbAccessToken(code)
                .map(wb -> weiboLogin(wb.getUid(), wb.getAccess_token()))
                .orElse(ResultVO.WEIBO_AUTH_FAILED);
    }

    /***
     * 微信验证
     * @param openId
     * @param openToken
     * @return
     */
    @RequestMapping(value = "/sso/wechat", method = RequestMethod.POST)
    @Transactional
    public ResultVO wechatLogin(String openId, String unionId, String openToken, Integer type) {
        if (userService.unionIdExist(unionId)) {
            Optional<User> userOptional = userService.unionIdExist(unionId, type) ?
                    userRepository.findByUnionIdAndType(unionId, type)
                            .flatMap(user1 -> user1.wechatLogin(openId, openToken, type)) :
                    userRepository.findByUnionId(unionId)
                            .flatMap(user -> user.wechatExtLogin(openId, openToken, unionId, type));
            return userOptional
                    .map(user -> {
                        UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
                        userInfoTokenVO.setRegist(false);
                        return userInfoTokenVO;
                    })
                    .map(ResultVO::new)
                    .orElse(ResultVO.WECHAT_AUTH_FAILED);
        } else {
            return User.wechatRegister(openId, unionId, openToken, type)
                    .map(user -> {
                        UserInfoTokenVO userInfoTokenVO = user.getUserInfoTokenVO();
                        userInfoTokenVO.setRegist(true);
                        return userInfoTokenVO;
                    })
                    .map(ResultVO::new)
                    .orElse(ResultVO.WECHAT_AUTH_FAILED);
        }
    }

    @RequestMapping(value = "/sso/wechat/code", method = RequestMethod.POST)
    @Transactional
    public ResultVO wechatLoginCode(String code, Integer type) {
        return ssoService
                .wxPubAccessToken(code, type)
                .map(wx -> wechatLogin(wx.getOpenid(), wx.getUnionid(), wx.getAccess_token(), type))
                .orElse(ResultVO.WECHAT_AUTH_FAILED);
    }

    /**
     * 重置密码
     */
    @RequestMapping(value = "/resetPassword", method = RequestMethod.POST)
    @Transactional
    public ResultVO resetPassword(String secureToken, String password) {
        return userRepository
                .findBySecureToken(secureToken)
                .flatMap(user1 -> user1.resetPassword(secureToken, password))
                .map(User::getUserInfoTokenVO)
                .map(ResultVO::new)
                .orElse(ResultVO.SECURE_TOKEN_ERROR);
    }

    /**
     * 检查手机号
     */
    @RequestMapping(value = "/check/phone", method = RequestMethod.GET)
    public ResultVO<Map<String, Boolean>> checkPhone(String phone) {
        Boolean exist = userService.phoneExist(phone);
        Map<String, Boolean> map = new HashMap<>();
        map.put("exist", exist);
        return new ResultVO<>(map);
    }

    /**
     * 检查邮箱
     */
    @RequestMapping(value = "/check/email", method = RequestMethod.GET)
    public ResultVO<Map<String, Boolean>> checkEmail(String email) {
        Boolean exist = userService.emailExist(email);
        Map<String, Boolean> map = new HashMap<>();
        map.put("exist", exist);
        return new ResultVO<>(map);
    }

    private ResultVO tokenResult(Optional<String> token) {
        if (!token.isPresent()) {
            return ResultVO.VERIFY_CODE_ERROR;
        } else {
            Map<String, String> result = new HashMap<>();
            result.put("secureToken", token.get());
            return new ResultVO<>(result);
        }
    }
}
